package co.edu.udistrital;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JPanel;

/**
 */
public class Tablero extends JPanel {

	/**
	 * Serial Version UID.
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * Color minimo para los cuadros
	 */
	public static final int COLOR_MIN = 35;
	
	/**
	 * Color maximo para los cuadros
	 */
	public static final int COLOR_MAX = 255 - COLOR_MIN;
	
	/**
	 * El ancho del borde para el tablero de juego
	 */
	private static final int ANCHO_BORDE = 5;
	
	/**
	 * Numero de columnas en el tablero
	 */
	public static final int TOTAL_COLUMNAS = 10;
		
	/**
	 * El numero de filas visibles en el tablero
	 */
	private static final int TOTAL_FILAS_VISIBLES = 20;
	
	/**
	 * El numero de filas ocultas en el tablero
	 */
	private static final int NUMERO_FILAS_OCULTAS = 2;
	
	/**
	 * Total filas en el tablero
	 */
	public static final int TOTAL_FILAS = TOTAL_FILAS_VISIBLES + NUMERO_FILAS_OCULTAS;
	
	/**
	 * Numero de pixeles para cada cuadro
	 */
	public static final int TAMANIO_CUADRO = 24;
	
	/**
	 * Ancho de la sombra del cuadro.
	 */
	public static final int SOMBRA_CUADRO = 4;
	
	/**
	 * Coordenada x del centro del tablero
	 */
	private static final int CENTRO_TABLERO_X = TOTAL_COLUMNAS * TAMANIO_CUADRO / 2;
	
	/**
	 * Coordenada y del centro del tablero
	 */
	private static final int CENTRO_TABLERO_Y = TOTAL_FILAS_VISIBLES * TAMANIO_CUADRO / 2;
		
	/**
	 * Ancho total del panel
	 */
	public static final int ANCHO_PANEL = TOTAL_COLUMNAS * TAMANIO_CUADRO + ANCHO_BORDE * 2;
	
	/**
	 * Alto total del panel
	 */
	public static final int ALTO_PANEL = TOTAL_FILAS_VISIBLES * TAMANIO_CUADRO + ANCHO_BORDE * 2;
	
	/**
	 * Fuente grande
	 */
	private static final Font FUENTE_GRANDE = new Font("Tahoma", Font.BOLD, 16);

	/**
	 * Fuente normal
	 */
	private static final Font FUENTE_NORMAL = new Font("Tahoma", Font.BOLD, 12);
	
	/**
	 * Instancia de la logica del juego
	 */
	private Tetris tetris;
	
	/**
	 * Cuadros que forman el tablero.
	 */
	private Figura[][] cuadrosTablero;
		
	/**
	 * Constructor
	 * Crea una nueva instancia del tablero
	 */
	public Tablero(Tetris tetris) {
		this.tetris = tetris;
		this.cuadrosTablero = new Figura[TOTAL_FILAS][TOTAL_COLUMNAS];
		
		setPreferredSize(new Dimension(ANCHO_PANEL, ALTO_PANEL));
		setBackground(Color.BLACK);
	}
	
	/**
	 * Reinicia el tablero
	 */
	public void limpiar() {
		for(int i = 0; i < TOTAL_FILAS; i++) {
			for(int j = 0; j < TOTAL_COLUMNAS; j++) {
				cuadrosTablero[i][j] = null;
			}
		}
	}
	
	/**
	 * Determina si una figura puede ser colocada en unas coordenadas
	 * @param figura La figura
	 * @param x La coordenada en x.
	 * @param y La coordenada en y.
	 * @param rotacion La rotacion de la figura.
	 * @return Si es una posicion valida o no
	 */
	public boolean esPosicionValida(Figura figura, int x, int y, int rotacion) {
				
		//Columna valida
		if(x < -figura.obtenerInsercionEnIzquierda(rotacion) || x + figura.getDimension() - figura.obtenerInsercionEnDerecha(rotacion) >= TOTAL_COLUMNAS) {
			return false;
		}
		
		//Fila valida
		if(y < -figura.obtenerInsercionSuperior(rotacion) || y + figura.getDimension() - figura.obtenerInsercionInferior(rotacion) >= TOTAL_FILAS) {
			return false;
		}
		
		/*
		 * Que no exista nada en ese cuadro
		 */
		for(int columna = 0; columna < figura.getDimension(); columna++) {
			for(int fila = 0; fila < figura.getDimension(); fila++) {
				if(figura.existeCuadro(columna, fila, rotacion) && existeFiguraEnElCuadro(x + columna, y + fila)) {
					return false;
				}
			}
		}
		return true;
	}
	
	/**
	 * Agrega una figura al tablero
	 * @param figura La figura a agreagar
	 * @param x La coordenada en x
	 * @param y La coordenada en y
	 * @param rotacion La rotacion de la figura
	 */
	public void agregarFigura(Figura figura, int x, int y, int rotacion) {
		for(int columna = 0; columna < figura.getDimension(); columna++) {
			for(int fila = 0; fila < figura.getDimension(); fila++) {
				if(figura.existeCuadro(columna, fila, rotacion)) {
					setFigura(columna + x, fila + y, figura);
				}
			}
		}
	}
	
	/**
	 * Comprueba el tablero para saber si se pueden eliminar lineas
	 * @return El numero de lineas que fueron borradas
	 */
	public int verificarLineas() {
		int lineasCompletadas = 0;
		
		/*
		 * Se recorre linea a linea para verificar si se puede borrar
		 * el metodo que verifica de una vez borra la linea
		 */
		for(int i = 0; i < TOTAL_FILAS; i++) {
			if(verificarLinea(i)) {
				lineasCompletadas++;
			}
		}
		return lineasCompletadas;
	}
			
	/**
	 * Verifica si la linea esat completa
	 * @param numeroLinea linea a verificar
	 * @return Si la linea esta completa o no
	 */
	private boolean verificarLinea(int numeroLinea) {
		
		for(int columna = 0; columna < TOTAL_COLUMNAS; columna++) {
			if(!existeFiguraEnElCuadro(columna, numeroLinea)) {
				return false;
			}
		}
		
		/*
		 * Se copian las lineas una por una de arriba a abajo
		 */
		for(int linea = numeroLinea - 1; linea >= 0; linea--) {
			for(int col = 0; col < TOTAL_COLUMNAS; col++) {
				setFigura(col, linea + 1, getFigura(col, linea));
			}
		}
		return true;
	}
	
	
	/**
	 * Verifica si el cuadro tine una figura
	 * @param x La coordenada en x
	 * @param y La coordenada en y
	 * @return Si el cuadro tiene o no una figura
	 */
	private boolean existeFiguraEnElCuadro(int x, int y) {
		return cuadrosTablero[y][x] != null;
	}
	
	/**
	 * Llena un cuadro con una figura
	 * @param x La coordenada en x.
	 * @param y La coordenada en y.
	 * @param figura Figura a colocar en el cuadro.
	 */
	private void setFigura(int  x, int y, Figura figura) {
		cuadrosTablero[y][x] = figura;
	}
		
	/**
	 * Obtener figura
	 * @param x Coordenada en x.
	 * @param y Coordenada en y.
	 * @return El cuadrado
	 */
	private Figura getFigura(int x, int y) {
		return cuadrosTablero[y][x];
	}
	
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		
		g.translate(ANCHO_BORDE, ANCHO_BORDE);
		
		/*
		 * Piinta el tablero dependiendo del estado en que se encuentre
		 */
		if(tetris.estaPausado()) {
			g.setFont(FUENTE_GRANDE);
			g.setColor(Color.WHITE);
			String msg = "PAUSADO";
			g.drawString(msg, CENTRO_TABLERO_X - g.getFontMetrics().stringWidth(msg) / 2, CENTRO_TABLERO_Y);
		} else if(tetris.esNuevoJuego() || tetris.esJuegoPerdido()) {
			g.setFont(FUENTE_GRANDE);
			g.setColor(Color.WHITE);
			
			String msg = tetris.esNuevoJuego() ? "JUGAR :)" : "JUEGO PERDIDO :(";
			g.drawString(msg, CENTRO_TABLERO_X - g.getFontMetrics().stringWidth(msg) / 2, 150);
			g.setFont(FUENTE_NORMAL);
			msg ="";
			g.drawString(msg, CENTRO_TABLERO_X - g.getFontMetrics().stringWidth(msg) / 2, 300);
		} else {
			
			/*
			 * Pinta los cuadros en el tablero
			 */
			for(int x = 0; x < TOTAL_COLUMNAS; x++) {
				for(int y = NUMERO_FILAS_OCULTAS; y < TOTAL_FILAS; y++) {
					Figura tile = getFigura(x, y);
					if(tile != null) {
						dibujarCuadro(tile, x * TAMANIO_CUADRO, (y - NUMERO_FILAS_OCULTAS) * TAMANIO_CUADRO, g);
					}
				}
			}
			
			/*
			 * Se pintan las figuras acutales
			 */
			Figura figura = tetris.getActualFigura();
			int columnaFigura = tetris.getColumnaActual();
			int filaFigura = tetris.getFilaActual();
			int rotacionFigura = tetris.getRotacionActual();
			
			//Dibuja las figuras en el tablero
			for(int columna = 0; columna < figura.getDimension(); columna++) {
				for(int fila = 0; fila < figura.getDimension(); fila++) {
					if(filaFigura + fila >= 2 && figura.existeCuadro(columna, fila, rotacionFigura)) {
						dibujarCuadro(figura, (columnaFigura + columna) * TAMANIO_CUADRO, (filaFigura + fila - NUMERO_FILAS_OCULTAS) * TAMANIO_CUADRO, g);
					}
				}
			}
			
			/*
			 * Pinta la figura de ayuda
			 */
			Color base = figura.getColorBase();
			base = new Color(base.getRed(), base.getGreen(), base.getBlue(), 20);
			for(int filaMasBaja = filaFigura; filaMasBaja < TOTAL_FILAS; filaMasBaja++) {
				//Si no se sobre pone intenta la siguiente fila
				if(esPosicionValida(figura, columnaFigura, filaMasBaja, rotacionFigura)) {					
					continue;
				}
				
				filaMasBaja--;
				
				//Dibuja la figura ayuda
				for(int columna = 0; columna < figura.getDimension(); columna++) {
					for(int fila = 0; fila < figura.getDimension(); fila++) {
						if(filaMasBaja + fila >= 2 && figura.existeCuadro(columna, fila, rotacionFigura)) {
							dibujarCuadro(base, base.brighter(), base.darker(), (columnaFigura + columna) * TAMANIO_CUADRO, (filaMasBaja + fila - NUMERO_FILAS_OCULTAS) * TAMANIO_CUADRO, g);
						}
					}
				}
				
				break;
			}
			
			/*
			 * Dibuja la cuadricula para guiar a los usuarios
			 */
			g.setColor(Color.DARK_GRAY);
			for(int x = 0; x < TOTAL_COLUMNAS; x++) {
				for(int y = 0; y < TOTAL_FILAS_VISIBLES; y++) {
					g.drawLine(0, y * TAMANIO_CUADRO, TOTAL_COLUMNAS * TAMANIO_CUADRO, y * TAMANIO_CUADRO);
					g.drawLine(x * TAMANIO_CUADRO, 0, x * TAMANIO_CUADRO, TOTAL_FILAS_VISIBLES * TAMANIO_CUADRO);
				}
			}
		}
		
		/*
		 * Dibuja el contorno del tablero.
		 */
		g.setColor(Color.WHITE);
		g.drawRect(0, 0, TAMANIO_CUADRO * TOTAL_COLUMNAS, TAMANIO_CUADRO * TOTAL_FILAS_VISIBLES);
	}
	
	/**
	 * Dibuja un cuadro en el tablero
	 * @param figura La figura del cuadro que se quiere pintar
	 * @param x The column.
	 * @param y The row.
	 * @param g The graphics object.
	 */
	private void dibujarCuadro(Figura figura, int x, int y, Graphics g) {
		dibujarCuadro(figura.getColorBase(), figura.getColorClaro(), figura.getColorOscuro(), x, y, g);
	}
	
	/**
	 * Dibuja un cuadro en el tablero
	 * @param colorBase color base.
	 * @param colorClaro Color claro del cuadro.
	 * @param colorOscuro Color oscuro del cuadro.
	 * @param x La columna.
	 * @param y La fila.
	 * @param g El objeto graphics
	 */
	private void dibujarCuadro(Color colorBase, Color colorClaro, Color dark, int x, int y, Graphics g) {
		
		/*
		 * Llena todo el cuadro con el color base
		 */
		g.setColor(colorBase);
		g.fillRect(x, y, TAMANIO_CUADRO, TAMANIO_CUADRO);
		
		/*
		 * Llena los bordes inferiores y derecho con el color oscuro
		 */
		g.setColor(dark);
		g.fillRect(x, y + TAMANIO_CUADRO - SOMBRA_CUADRO, TAMANIO_CUADRO, SOMBRA_CUADRO);
		g.fillRect(x + TAMANIO_CUADRO - SOMBRA_CUADRO, y, SOMBRA_CUADRO, TAMANIO_CUADRO);
		
		/*
		 * Llena los bordes superior e izquierdo con el color claro
		 */
		g.setColor(colorClaro);
		for(int i = 0; i < SOMBRA_CUADRO; i++) {
			g.drawLine(x, y + i, x + TAMANIO_CUADRO - i - 1, y + i);
			g.drawLine(x + i, y, x + i, y + TAMANIO_CUADRO - i - 1);
		}
	}

}
